﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.IO.Compression;
using System.Collections;
using System.IO.Packaging;
using System.Xml.Linq;
using SmartSpace.Core;

namespace SmartSpace.Packaging
{
    public class PackageUtil
    {
        private const long BUFFER_SIZE = 4096;

        public static void CompressFiles(string folderPath, IEnumerable<string> fileNames, string zipFileName)
        {
            if (File.Exists(zipFileName))
            {
                File.Delete(zipFileName);
            }

            using (Package zip = System.IO.Packaging.Package.Open(zipFileName, FileMode.CreateNew))
            {
                foreach (string file in fileNames)
                {
                    CompressFile(zip, folderPath, file);
                }
            }
        }

        public static void CompressFile(Package zip, string folderPath, string fileToAdd)
        {
            string destFilename = Path.Combine(folderPath, fileToAdd);// ".\\" + Path.GetFileName(fileToAdd);
            Uri uri = PackUriHelper.CreatePartUri(new Uri(fileToAdd, UriKind.Relative));
            if (zip.PartExists(uri))
            {
                zip.DeletePart(uri);
            }
            PackagePart part = zip.CreatePart(uri, "", CompressionOption.Normal);
            using (FileStream fileStream = new FileStream(destFilename, FileMode.Open, FileAccess.Read))
            {
                using (Stream dest = part.GetStream())
                {
                    CopyStream(fileStream, dest);
                }
            }
        }

        public static void DecompressFile(string zipFilename, string outPath)
        {
            using (Package zip = System.IO.Packaging.Package.Open(zipFilename, FileMode.Open))
            {
                foreach (PackagePart part in zip.GetParts())
                {
                    string outFileName = Path.Combine(outPath, part.Uri.OriginalString.Remove(0,1));
                    FileInfo fi = new FileInfo(outFileName);
                    if (!Directory.Exists(fi.DirectoryName))
                    {
                        Directory.CreateDirectory(fi.DirectoryName);
                    }
                    using (System.IO.FileStream outFileStream = new System.IO.FileStream(outFileName, FileMode.CreateNew))
                    {
                        using (Stream inFileStream = part.GetStream())
                        {
                            CopyStream(inFileStream, outFileStream);
                        }
                    }
                }
            }
        }

        public static void DecompressFile(Stream stream, string outPath)
        {
            using (Package zip = System.IO.Packaging.Package.Open(stream, FileMode.Open))
            {
                foreach (PackagePart part in zip.GetParts())
                {
                    string outFileName = Path.Combine(outPath, part.Uri.OriginalString.Remove(0, 1));
                    FileInfo fi = new FileInfo(outFileName);
                    if (!Directory.Exists(fi.DirectoryName))
                    {
                        Directory.CreateDirectory(fi.DirectoryName);
                    }
                    using (System.IO.FileStream outFileStream = new System.IO.FileStream(outFileName, FileMode.CreateNew))
                    {
                        using (Stream inFileStream = part.GetStream())
                        {
                            CopyStream(inFileStream, outFileStream);
                        }
                    }
                }
            }
        }

        private static void CopyStream(System.IO.Stream inputStream, System.IO.Stream outputStream)
        {
            long bufferSize = inputStream.Length < BUFFER_SIZE ? inputStream.Length : BUFFER_SIZE;
            byte[] buffer = new byte[bufferSize];
            int bytesRead = 0;
            long bytesWritten = 0;
            while ((bytesRead = inputStream.Read(buffer, 0, buffer.Length)) != 0)
            {
                outputStream.Write(buffer, 0, bytesRead);
                bytesWritten += bufferSize;
            }
        }

        public static XTemplate ParseXTML(string filePath)
        {
            FileInfo fi = new FileInfo(filePath);
            XDocument xDoc = XDocument.Load(filePath);
            XElement root = xDoc.Element("template");
            IEnumerable<XElement> xElements = root.Elements("file");
            IEnumerable<XElement> folderElements = root.Elements("folder");

            XTemplate template = new XTemplate();
            template.Id = new Guid(root.Attribute("id").Value);
            template.Caption = root.Attribute("caption").Value;
            template.Name = root.Attribute("name").Value;
            template.Package = root.Attribute("package").Value;
            template.Version = root.Attribute("version").Value;
            template.Type = root.Attribute("type").Value;

            foreach (XElement item in xElements)
            {
                XFile xFile = new XFile
                {
                    Path = item.Attribute("path").Value,
                    Publish = item.Attribute("publish").Value
                };

                template.Files.Add(xFile);
            }

            foreach (XElement folderElement in folderElements)
            {
                string path = folderElement.Attribute("path").Value;
                string publish = folderElement.Attribute("publish").Value;

                DirectoryInfo di = new DirectoryInfo(Path.Combine(fi.DirectoryName, path));
                ParseDirectory(template, di, path, publish);
            }

            return template;
        }

        public static XModule ParseXMML(string filePath)
        {
            FileInfo fi = new FileInfo(filePath);

            XDocument xDoc = XDocument.Load(filePath);

            XElement root = xDoc.Element("module");
            IEnumerable<XElement> xElements = root.Elements("file");
            IEnumerable<XElement> folderElements = root.Elements("folder");

            XModule module = new XModule();
            module.Id = new Guid(root.Attribute("id").Value);
            module.Caption = root.Attribute("caption").Value;
            module.Name = root.Attribute("name").Value;
            module.Package = root.Attribute("package").Value;
            module.Version = root.Attribute("version").Value;
            module.Type = root.Attribute("type").Value;
            module.Views = new List<XView>();
            IEnumerable<XElement> viewElements = root.Elements("view");

            foreach (XElement viewElement in viewElements)
            {
                XView view = new XView();
                view.Id = new Guid(viewElement.Attribute("id").Value);
                view.Caption = viewElement.Attribute("caption").Value;
                view.Name = viewElement.Attribute("name").Value;
                view.ModuleId = module.Id;
                module.Views.Add(view);
            }

            foreach (XElement item in xElements)
            {
                XFile xFile = new XFile
                {
                    Path = item.Attribute("path").Value,
                    Publish = item.Attribute("publish").Value
                };

                module.Files.Add(xFile);
            }

            foreach (XElement folderElement in folderElements)
            {
                string path = folderElement.Attribute("path").Value;
                string publish = folderElement.Attribute("publish").Value;

                DirectoryInfo di = new DirectoryInfo(Path.Combine(fi.DirectoryName, path));
                ParseDirectory(module, di, path, publish);
            }

            return module;
        }

        private static void ParseDirectory(IFilePackage package, DirectoryInfo parentDir, string path, string publish)
        {
            DirectoryInfo[] subDirs = parentDir.GetDirectories();
            foreach (var subDir in subDirs)
            {
                string subPath = Path.Combine(path, subDir.Name);
                string subPublish = Path.Combine(publish, subDir.Name);
                ParseDirectory(package, subDir, subPath, subPublish);
            }

            FileInfo[] files = parentDir.GetFiles();
            foreach (var file in files)
            {
                XFile xFile = new XFile
                {
                    Path = Path.Combine(path, file.Name),
                    Publish = Path.Combine(publish, file.Name)
                };

                package.Files.Add(xFile);
            }
        }
    }

}
